type FunctionType = (...args: any[]) => void;


export function debounce(fun: FunctionType, delay: number = 500, isImmediate: boolean = true): FunctionType {
  let timer: number | null = null; // 定时器
  let immediateTimer: number | null = null; // 继续首次触发的定时器
  let _isImmediate: boolean = isImmediate; // 第一次触发是否立即
  // 返回的函数类型是 FunctionType，即接收任意参数并无返回值的函数
  return function (this: any, ...rest: any[]): void {
    timer && clearTimeout(timer);
    immediateTimer && clearTimeout(immediateTimer);

    if (_isImmediate) {
      fun.call(this, ...rest);
      _isImmediate = false;
      timer = null;

      immediateTimer = setTimeout(() => {
        _isImmediate = isImmediate;
        immediateTimer = null;
      }, delay);
    } else {
      timer = setTimeout(() => {
        fun.call(this, ...rest);
        _isImmediate = isImmediate;
        timer = null;
        immediateTimer = null;
      }, delay);
    }
  };
}



export function debounce2<T extends (...args: any[]) => any>(
  fn: T,
  delay: number = 500,
  immediate: boolean = false
): (...args: Parameters<T>) => void {
  let timeoutId: NodeJS.Timeout | null = null;
  let lastCallTime: number | null = null;

  return function(this: any, ...args: Parameters<T>): void {
    const now = Date.now();

    if (timeoutId) {
      clearTimeout(timeoutId);
    }

    if (immediate && (!lastCallTime || now - lastCallTime >= delay)) {
      fn.apply(this, args);
      lastCallTime = now;
    } else {
      timeoutId = setTimeout(() => {
        fn.apply(this, args);
        lastCallTime = null;
        timeoutId = null;
      }, delay);
    }
  };
}

export function throttle(fun: FunctionType, delay: number = 500, immediate: boolean = true): FunctionType {
  let previousTimeStamp: number = 0; // 上一次点击的时间戳
  let timer: number | null = null; // 定时器
  return function (this: any, ...rest: any[]): void {
    if (immediate) {
      let nowTimeStamp: number = +new Date(); // 获取当前点击的时间戳
      if (nowTimeStamp - previousTimeStamp > delay) {
        fun.call(this, ...rest);
        previousTimeStamp = nowTimeStamp;
      }
    } else {
      if (!timer) {
        timer = setTimeout(() => {
          fun.call(this, ...rest);
          timer = null;
        }, delay);
      }
    }
  };
}